package cc.owoo.godpen.network;

import cc.owoo.godpen.structure.CustomLinkedList;
import cc.owoo.godpen.thread.Threads;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

/**
 * 逆向Socket
 * Created by nimensei
 * 2022-05-14 上午 01:58
 **/
public class ReverseSocket {
    private final Object lock = new Object();// 线程锁
    private final String ip;// IP地址
    private final int port;// 端口号
    private OnAccept onAccept;// 新连接监听器
    private Runnable onOffline;// 进入连接状态
    private Runnable onOnline;// 进入创建状态
    private OnAccept onSuccess;// 连接成功
    private int maxCount = 16;// 最大连接数
    private final CustomLinkedList<Socket> socketList = new CustomLinkedList<>();// 连接池
    private State state;// 状态
    private boolean isClose;// 是否关闭
    private int createThreadID;// 建立连接的线程自增ID

    private enum State {
        CONNECT,// 连接状态
        CREATE // 创建状态
    }

    /**
     * 创建逆向socket
     *
     * @param ip   服务器IP
     * @param port 服务器端口
     */
    public ReverseSocket(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    /**
     * 启动
     * <p>
     * 函数会在线程中执行
     */
    public void start() {
        offline(false);
    }

    /**
     * 重启
     */
    public void restart() {
        offline(false);
    }

    /**
     * 进入重连状态
     */
    private void offline(boolean delay) {
        synchronized (lock) {
            if (state == State.CONNECT || isClose)
                return;
            state = State.CONNECT;
        }
        ++createThreadID;
        clearSocketList();
        if (onOffline != null)
            onOffline.run();
        if (delay)
            Threads.delay(1000);
        while (state == State.CONNECT) {
            if (establish())
                break;
            Threads.delay(1000);
        }
        Threads.run(this::online);
    }


    /**
     * 进入构建连接池状态
     */
    private void online() {
        synchronized (lock) {
            if (state == State.CREATE || isClose)
                return;
            state = State.CREATE;
        }
        int id = ++createThreadID;
        if (onOnline != null)
            onOnline.run();
        build:
        while (state == State.CREATE && !isClose && id == createThreadID) {
            while (socketList.size() < maxCount) {
                if (state != State.CREATE)
                    break build;
                if (!establish()) {
                    offline(true);
                    break build;
                }
            }
            Threads.delay(1);
        }
    }

    /**
     * 清空连接列表
     */
    private void clearSocketList() {
        synchronized (socketList) {
            socketList.forEach(node -> {
                Socket socket = node.value();
                if (!socket.isClosed())
                    try {
                        socket.close();
                    } catch (IOException ignored) {
                    }
            });
            socketList.clear();
        }
    }

    /**
     * 建立新连接
     *
     * @return 是否建立成功
     */
    private boolean establish() {
        Socket socket;
        try {
            socket = createSocket();
        } catch (IOException e) {
            return false;
        }
        CustomLinkedList.Node<Socket> node;
        synchronized (socketList) {
            node = socketList.addLast(socket);
        }
        Threads.run(() -> waitAccept(socket, node));
        return true;
    }

    /**
     * 等待连接
     *
     * @param socket socket对象
     */
    private void waitAccept(Socket socket, CustomLinkedList.Node<Socket> node) {
        try {
            InputStream input = socket.getInputStream();
            while (true)
                if (input.read() == 1)
                    break;
        } catch (IOException e) {
            offline(true);
            return;
        }
        synchronized (socketList) {
            if (node.isExist())
                node.remove();
        }
        if (onAccept != null)
            onAccept.accept(socket);
    }

    /**
     * 创建对话socket
     *
     * @return 对话socket
     * @throws IOException 创建异常
     */
    private Socket createSocket() throws IOException {
        return new Socket(ip, port);
    }

    /**
     * 设置最大连接数
     *
     * @param maxCount 最大连接数
     */
    public void setMaxCount(int maxCount) {
        if (maxCount < 1)
            throw new IllegalArgumentException("最大连接数不能小于1");
        this.maxCount = maxCount;
    }

    /**
     * 获取最大连接数
     *
     * @return 最大连接数
     */
    public int getMaxCount() {
        return maxCount;
    }

    /**
     * 设置新连接监听器
     *
     * @param onAccept 新连接监听器
     */
    public void setOnAccept(OnAccept onAccept) {
        this.onAccept = onAccept;
    }

    /**
     * 获取新连接监听器
     *
     * @return 新连接监听器
     */
    public OnAccept getOnAccept() {
        return onAccept;
    }

    /**
     * 设置连接状态监听器
     *
     * @param onOffline 连接状态监听器
     */
    public void setOnOffline(Runnable onOffline) {
        this.onOffline = onOffline;
    }

    /**
     * 获取连接状态监听器
     *
     * @return 连接状态监听器
     */
    public Runnable getOnOffline() {
        return onOffline;
    }

    /**
     * 设置创建状态监听器
     *
     * @param onOnline 创建状态监听器
     */
    public void setOnOnline(Runnable onOnline) {
        this.onOnline = onOnline;
    }

    /**
     * 获取创建状态监听器
     *
     * @return 创建状态监听器
     */
    public Runnable getOnOnline() {
        return onOnline;
    }

    /**
     * 设置连接成功监听器
     *
     * @param onSuccess 连接成功监听器
     */
    public void setOnSuccess(OnAccept onSuccess) {
        this.onSuccess = onSuccess;
    }

    /**
     * 获取连接成功监听器
     *
     * @return 连接成功监听器
     */
    public OnAccept getOnSuccess() {
        return onSuccess;
    }

    /**
     * 获取当前连接数
     *
     * @return 连接数
     */
    public int count() {
        return socketList.size();
    }

    /**
     * 关闭socket
     */
    public void close() {
        isClose = true;
    }

    /**
     * 新连接监听器
     */
    public interface OnAccept {
        void accept(Socket socket);
    }
}
